#ifndef XCOLLADA_H
#define XCOLLADA_H
#include <vector>
#include <string>
#include "../../../RTE/RTE/RTEComponent/GameCore/XMath.h"
#include <d3d11_1.h>
namespace XColladaNamespace
{
	enum ShaderUniformType
	{
		SUT_unknown = 0,
		SUT_float,
		SUT_float2,
		SUT_float3,
		SUT_float4,
		SUT_matrix2x2,
		SUT_matrix3x3,
		SUT_matrix4x4,
		SUT_Texture2D,
		SUT_SamplerState,
		SUT_int,
		SUT_count
	};
	enum ShaderVaryingType
	{
		SVT_unknown = 0,
		SVT_float,
		SVT_float2,
		SVT_float3,
		SVT_float4,
		SVT_matrix,
		SVT_count
	};
	enum WrapMode
	{
		WM_unknown = -1,
		WM_Repeat = D3D11_TEXTURE_ADDRESS_WRAP, //DX: WRAP
		WM_Mirror = D3D11_TEXTURE_ADDRESS_MIRROR,
		WM_Clamp = D3D11_TEXTURE_ADDRESS_CLAMP,
		WM_ClampToEdge = D3D11_TEXTURE_ADDRESS_CLAMP,
		//WM_count
	};
	enum FilterMode
	{
		FM_unknown = -1,
		FM_Nearest = D3D11_FILTER_MIN_MAG_MIP_POINT,
		FM_Linear = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT,
		FM_Nearest_Mipmap_Nearest = D3D11_FILTER_MIN_MAG_MIP_POINT,
		FM_Nearest_Mipmap_Linear = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR,
		FM_Linear_Mipmap_Nearest = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT,
		FM_Linear_Mipmap_Linear = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR,
		//FM_count
	};
	enum Semantic
	{
		SMT_unknown,
		SMT_Vertex,
		SMT_Position,
		SMT_Texcoord,
		SMT_Normal,
		SMT_Tangent,
		SMT_Binormal,
	};
	struct Uniform
	{
		std::string name;
		ShaderUniformType type;
		float floatData[16];
		int intData;
		WrapMode wrapModeU;
		WrapMode wrapModeV;
		WrapMode wrapModeW;
		FilterMode filterMin;
		FilterMode filterMag;
		std::string textureName;
		Uniform()
		{
			wrapModeU = wrapModeV = wrapModeW = WM_Clamp;
			filterMin = filterMag = FM_Linear;
		}
	};
	struct Varying
	{
		std::string  name;
		ShaderVaryingType type;
	};
	struct Effect
	{
		std::string id;
		std::string  effectName;
		std::vector<Uniform*> uniforms;
		std::vector<Uniform*> uniformsIgnored;
		std::vector<Varying*> varyings;
		~Effect()
		{
			if(uniforms.size() > 0)
			{
				for(int i = 0; i < uniforms.size(); i++)
				{
					delete uniforms[i];
				}
				uniforms.clear();
			}
			if(varyings.size() > 0)
			{
				for(int i = 0; i < varyings.size(); i++)
				{
					delete varyings[i];
				}
				varyings.clear();
			}
		}
	};
	struct Material
	{
		std::string id;
		std::string name;
		Effect* effect;
		~Material()
		{
			//delete in XCollada
			/*
			if(effect)
			{
				delete effect;
				effect = NULL;
			}
			*/
		}
	};
	
	struct MeshSource
	{
		std::string id;
		unsigned int floatCount;
		float* data;
		unsigned int stride;
		MeshSource()
		{
			data = 0;
			floatCount = 0;
			stride = 0;
		}
		MeshSource( const MeshSource& other )
		{
			this->id = other.id;
			if(this->floatCount > 0)
			{
				this->floatCount = other.floatCount;
				this->data = new float[this->floatCount];
				memcpy(this->data, other.data, this->floatCount*sizeof(float));
			}
			else
			{
				this->floatCount = 0;
			}
		}
		~MeshSource()
		{
			if(data) delete[] data;
			data = 0;
			floatCount = 0;
		}
		MeshSource& operator=( const MeshSource& other )
		{
			if(data) delete[] data;
			data = 0;
			this->id = other.id;
			if(this->floatCount > 0)
			{
				this->floatCount = other.floatCount;
				this->data = new float[this->floatCount];
				memcpy(this->data, other.data, this->floatCount*sizeof(float));
			}
			else
			{
				this->floatCount = 0;
			}
			return *this;
		}
	};
	struct MeshVerticesInput
	{
		Semantic semantic;
		std::string source;
	};

	struct MeshTrianglesInput
	{
		Semantic semantic;
		MeshSource* meshSource;
		unsigned int offset;
		//unsigned int offsetInIndex;
	};
	struct MeshTriangles //Index buffer?
	{
		unsigned int indexCount;
		unsigned int triangleCount;
		unsigned int* indexArray;
		std::vector<MeshTrianglesInput*> inputs;
		unsigned int stride;
		MeshTriangles()
		{
			indexArray = 0;
			indexCount = 0;
		}
		~MeshTriangles()
		{
			if(indexArray) delete[] indexArray;
			indexArray = 0;
			indexCount = 0;
			//verticesCount = 0;
			for(int i = 0; i < inputs.size(); i++)
			{
				delete inputs[i];
			}
			inputs.clear();
		}
		MeshTriangles( const MeshTriangles& other )
		{
			if(other.indexCount > 0)
			{
				indexCount = other.indexCount;
				indexArray = new unsigned int[indexCount];
				memcpy(indexArray, other.indexArray, indexCount*sizeof(unsigned int));
			}
			else
			{
				indexArray = 0;
				indexCount = 0;
			}
			//verticesCount = other.verticesCount;
			inputs = other.inputs;
		}
		MeshTriangles& operator=( const MeshTriangles& other )
		{
			if(indexArray)
			{
				delete[] indexArray;
				indexArray = 0;
			}
			if(other.indexCount > 0)
			{
				indexCount = other.indexCount;
				indexArray = new unsigned int[indexCount];
				memcpy(indexArray, other.indexArray, indexCount*sizeof(unsigned int));
			}
			else
			{
				indexArray = 0;
				indexCount = 0;
			}
			//verticesCount = other.verticesCount;
			inputs = other.inputs;
			return *this;
		}
	};
	/*
	struct Source
	{

	};
	*/
	struct Vertices
	{
		std::string id;
		std::string name;
		std::vector<MeshVerticesInput*> inputs;
		~Vertices()
		{
			for(int i = 0; i< inputs.size(); i++)
			{
				delete inputs[i];
			}
			inputs.clear();
		}
	};
	struct Mesh
	{
		//std::vector<Source> sourceList;
		std::vector<MeshSource*> meshSources;
		//Vertices* vertices;
		std::vector<Vertices*> verticesList;
		MeshTriangles* triangles;
		Mesh()
		{
			//vertices = new Vertices();
			triangles = new MeshTriangles();
		}
		~Mesh()
		{
			for(int i = 0; i<meshSources.size(); i++)
			{
				delete meshSources[i];
			}
			meshSources.clear();
			/*if(vertices)
			{
				delete vertices;
				vertices = NULL;
			}*/
			if(triangles)
			{
				delete triangles;
				triangles = NULL;
			}
			for(int i = 0; i< verticesList.size(); i++)
			{
				delete verticesList[i];
			}
			verticesList.clear();
		}
	};
	struct Geometry
	{
		std::string id;
		std::string name;
		Mesh* mesh;
		Geometry()
		{
			mesh = new Mesh();
		}
		~Geometry()
		{
			if(mesh)
			{
				delete mesh;
				mesh = NULL;
			}
		}
	};
	struct InstanceGeometry
	{
		Geometry* geometry;
		Material* material;
	};
	struct Node
	{
		std::string id;
		std::string name;
		Matrix transform;
		Matrix transformNormal; //inverse transpose of transform
		std::vector<InstanceGeometry*> geometryList;
		Node()
		{
			transform.SetIdentity();
		}
		~Node()
		{
			for(int i = 0; i<geometryList.size(); i++)
			{
				delete geometryList[i];
			}
			geometryList.clear();
		}
	};
	struct VisualScene
	{
		std::vector<Node*> nodes;
		~VisualScene()
		{
			for(int i = 0; i<nodes.size(); i++)
			{
				delete nodes[i];
			}
			nodes.clear();
		}
	};
	struct XCollada
	{
		std::string version;
		std::vector<Geometry*> library_geometries;
		std::vector<Effect*> effects;
		std::vector<Material*> library_materials;
		std::vector<VisualScene*> visualScenes;
		void InitFromOpenCollada(char *buffer);
		void WriteToXColladaFile(char *filePath);
		void InitFromXCollada(char *buffer);
		~XCollada()
		{
			for(int i = 0; i<library_geometries.size(); i++)
			{
				delete library_geometries[i];
			}
			library_geometries.clear();
			for(int i = 0; i<effects.size(); i++)
			{
				delete effects[i];
			}
			effects.clear();
			for(int i = 0; i<library_materials.size(); i++)
			{
				delete library_materials[i];
			}
			library_materials.clear();
		}
	};
}
#endif